package com.motu.monstercity.server.game.logic;

import com.google.protobuf.InvalidProtocolBufferException;
import com.motu.monstercity.module.common.network.ResponseUtils;
import com.motu.monstercity.protocol.Common;
import com.motu.monstercity.protocol.Common.PBUserData;
import com.motu.monstercity.protocol.CsGameSystem;
import com.motu.monstercity.protocol.CsGameSystem.CSGameSystemRedPointResponse;
import com.motu.monstercity.server.game.base.AllParam;
import com.motu.monstercity.server.game.base.CommonUtils;
import com.motu.monstercity.server.game.base.LockKey;
import com.motu.monstercity.server.game.base.ProtoDataUtils;
import com.motu.monstercity.server.game.commondata.build.BuildConstant;
import com.motu.monstercity.server.game.commondata.maintask.MainTaskConstant;
import com.motu.monstercity.server.game.commondata.skill.SkillConstant;
import com.motu.monstercity.server.game.commondata.system.SystemConstant;
import com.motu.monstercity.server.game.commondata.world.WorldConstant;
import com.motu.monstercity.server.game.commondata.world.WorldEvent;
import com.motu.monstercity.server.game.commondata.world.WorldMonsterLevel;
import com.motu.monstercity.server.game.commondata.world.WorldRadar;
import com.motu.monstercity.server.game.log.LogType;
import com.motu.monstercity.server.game.userdata.*;
import com.motu.vertx.module.utility.main.MainService;
import com.motu.vertx.module.utility.model.EntityManager;
import com.motu.vertx.module.utility.redis.RedisTools;
import com.motu.vertx.module.utility.toolset.ErrorWord;
import com.motu.vertx.module.utility.toolset.TimeUtils;
import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.Future;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.core.shareddata.Lock;
import io.vertx.ext.auth.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

public class WorldManager extends WorldUserData {
    private static Logger logger = LoggerFactory.getLogger(WorldManager.class);

    // 初始化下发大世界行军模块的用户数据
    public static void loadInitWorldUserData(PBUserData.Builder pbUserData, UserInfo userInfo) {
        UserWorldInfo userWorldInfo = WorldManager.getUserWorldInfo(userInfo.getId());
        WorldManager.refreshWorldTeam(pbUserData, userInfo, userWorldInfo);// 刷新结算行军队列，未到达则不做更新，已达到，则更新队伍，清除事件坑位里的数据，如果已返回基地，则生成战报，更新干员信息，伤兵信息
    }

    // 判断大世界行军模块是否开启
    public static boolean checkWorldMoudleOpen(UserInfo userInfo) {
        UserBuild userBuild = BuildManager.getUserBuild(userInfo.getId(), BuildConstant.BUILD_ID_RADAR);
        if (userBuild != null) {
            return true;// 已解锁
        } else {
            return false;
        }
    }

    // 解锁事件坑位
    public static void unlockEventLocation(long userId, UserWorldInfo userWorldInfo, WorldRadar worldRadar) {
        // 事件来源类型1-系统整点自动刷新，locationid 从1--
        boolean isUpdate = false;
        if (userWorldInfo.getEventLocationNum() < worldRadar.getEventShowNum()) {
            for (int i=userWorldInfo.getEventLocationNum()+1; i<=worldRadar.getEventShowNum(); i++) {
                UserWorldEventLocation userWorldEventLocation = new UserWorldEventLocation(userId, i, WorldConstant.WORLD_EVENT_LOCATION_TYPE_0);
                userWorldEventLocation.doCreate();//1,2,3,4...8
            }
            userWorldInfo.putEventLocationNum(worldRadar.getEventShowNum());
            isUpdate = true;
        }

        // 事件来源类型2-使用道具121001生成额外事件，locationid 从2001--上限2008
        if (userWorldInfo.getEventLocationNumItem() < AllParam.WORLD_ITEM_121001_LIMIT) {
            int start = userWorldInfo.getEventLocationNumItem() + WorldConstant.WORLD_EVENT_LOCATION_ID_EXTRA;// locationid 从1001开始
            int end = start + AllParam.WORLD_ITEM_121001_LIMIT;
            for (int i=start; i<end; i++) {
                UserWorldEventLocation userWorldEventLocation = new UserWorldEventLocation(userId, i, WorldConstant.WORLD_EVENT_LOCATION_TYPE_1);
                userWorldEventLocation.doCreate();//2001,2002,2003...2007
            }
            userWorldInfo.putEventLocationNumItem(AllParam.WORLD_ITEM_121001_LIMIT);
        }

        // 事件来源紧急触发的，locationid 从3001--上限 = 行军队列的上限 紧急事件最多只有1个
        UserWorldEventLocation userWorldEventLocation = getUserWorldEventLocation(userId, WorldConstant.WORLD_EVENT_LOCATION_ID_URGENT);
        if (userWorldEventLocation == null) {
            userWorldEventLocation = new UserWorldEventLocation(userId, WorldConstant.WORLD_EVENT_LOCATION_ID_URGENT, WorldConstant.WORLD_EVENT_LOCATION_TYPE_6);
            userWorldEventLocation.doCreate();//3001
            userWorldInfo.putEventLocationNumUrgent(1);
        }

//        if (userWorldInfo.getEventLocationNumUrgent() < AllParam.WORLD_MARCH_NUM) {
//            int start = userWorldInfo.getEventLocationNumUrgent() + WorldConstant.WORLD_EVENT_LOCATION_ID_URGENT;// locationid 从3001开始
//            int end = start + AllParam.WORLD_MARCH_NUM;
//            for (int i=start; i<end; i++) {
//                UserWorldEventLocation userWorldEventLocation = new UserWorldEventLocation(userId, i, WorldConstant.WORLD_EVENT_LOCATION_TYPE_6);
//                userWorldEventLocation.doCreate();//2001,2002,2003...2007
//            }
//            userWorldInfo.putEventLocationNumUrgent(AllParam.WORLD_MARCH_NUM);
//        }

        if (isUpdate) {
            userWorldInfo.update();
        }
    }

    // 刷新行军队列，未到达则不做更新，已达到，则更新队伍，清除事件坑位里的数据，如果已返回基地，则生成战报，更新干员信息，伤兵信息
    public static void refreshWorldTeam(PBUserData.Builder pbUserData, UserInfo userInfo, UserWorldInfo userWorldInfo) {
        Lock lock = null;
        try {
            lock = MainService.getVertLock(LockKey.getWorldTeam(userInfo.getId()));
            List<UserWorldTeam> list = getUserWorldTeamList(userInfo.getId());
            if (list.size() == 0) {// 玩家初始时默认解锁一个行军队伍
                unlockTeam(pbUserData, userInfo, AllParam.WORLD_MARCH_NUM_INIT, LogType.SYSTEM_REWARD);
                list = getUserWorldTeamList(userInfo.getId());
            }

            for (UserWorldTeam userWorldTeam : list) {
                userWorldTeam.refreshData(pbUserData, userInfo, userWorldInfo);
                userWorldTeam.update();
                ProtoDataUtils.updatePBUserData(pbUserData, userWorldTeam, true);
            }
        } catch (Exception e) {
            return;
        } finally {// 释放锁
            MainService.releaseVertLock(lock);
        }
    }

    // 刷新事件，放到事件队列里面,事件最多持续15小时，间隔固定8个小时
    public static void refreshEvent(long userId, UserWorldInfo userWorldInfo, WorldRadar worldRadar) {
        clearWorldQueue(userId);// 先清空队列

        long curTime = TimeUtils.getCurTime();
        long sTime = TimeUtils.getStartTimeOfDay(curTime);// 今天的0点0分0秒
        long diff = WorldConstant.WORLD_EVENT_REFRESH_DIFFTIME;// 间隔8小时
        for (int i=3; i>-3; i--) {
            long eventTime = sTime + diff * i;
            if (eventTime <= curTime && eventTime > (curTime - WorldConstant.WORLD_EVENT_TIME)) {
                creatEvent(userId, worldRadar, eventTime);
            }
        }
        userWorldInfo.putEventRefreshTime(TimeUtils.getCurTime());// 记录下刷新时间
        userWorldInfo.update();
    }

    // 每天的0点8点16点整点刷新，过去的最新一次的刷新时间
    public static long getLastRefreshTime() {
        long curTime = TimeUtils.getCurTime();
        long sTime = TimeUtils.getStartTimeOfDay(curTime);// 今天的0点0分0秒
        long diff = WorldConstant.WORLD_EVENT_REFRESH_DIFFTIME;// 间隔8小时
        for (int i=3; i>-3; i--) {
            long eventTime = sTime + diff * i;// 每天的0点8点16点
            if (eventTime <= curTime) {
                return eventTime;
            }
        }
        return 0L;
    }

    /**
     * 更新所有的事件坑位
     * @param userInfo
     * @param userWorldInfo
     * @param worldRadar
     * @param isUserItem 使用道具/雷达升级时,强制刷新事件未到期也刷新，否则只刷新到期的事件
     */
    public static void updateAllEventLocation(UserInfo userInfo, UserWorldInfo userWorldInfo, WorldRadar worldRadar, List<UserWorldEventLocation> locationList, boolean isUserItem) {
        for (UserWorldEventLocation userWorldEventLocation : locationList) {
            if (userWorldEventLocation.isCommonType() && userWorldEventLocation.isNotTeamMarch()) {// 无队伍行军的事件坑位才可以刷新
                if (isUserItem || userWorldEventLocation.isExpire(TimeUtils.getCurTime()) || userWorldEventLocation.getEventId() == 0) {
                    WorldManager.updateEventLocationFromEventQueue(userInfo, userWorldInfo, userWorldEventLocation);
                }
            }
        }
    }

    // 把事件更新到事件坑位里，确定怪兽等级，随机怪兽战力, 删除事件队列里的数据
    public static void putEventToEventLocation(UserWorldInfo userWorldInfo, UserWorldEventQueue userWorldEventQueue, UserWorldEventLocation userWorldEventLocation) {
        userWorldEventLocation.putEventId(userWorldEventQueue.getEventId());
        userWorldEventLocation.putStartTime(userWorldEventQueue.getStartTime());
        userWorldEventLocation.putDistance(userWorldEventQueue.getDistance());
        userWorldEventLocation.putOnewayTime(getOneWayTime(userWorldEventQueue.getDistance()));
        userWorldEventLocation.putMonsterLevel(userWorldInfo.getMonsterLevel());
        long forces = getMonsterForces(userWorldEventQueue.getEventId(), userWorldInfo.getMonsterLevel());// 怪兽的战力 搜救事件有概率不战斗
        userWorldEventLocation.putForces(forces);
        userWorldEventLocation.putTotalForces(forces);
        userWorldEventLocation.putEventQueueDbid(userWorldEventQueue.getId());
        WorldEvent worldEvent = getWorldEvent(userWorldEventQueue.getEventId());
        if (worldEvent.getType() == WorldConstant.WORLD_EVENT_TYPE_4) {// 搜救事件会获得随机碎片，事先确定好奖励，客户端要表现用
            userWorldEventLocation.putRandomReward(worldEvent.takeRandomChip());// 随机碎片的奖励内容
        }
        if (worldEvent.getBoxId() > 0) {// 有配置全胜的宝箱奖励，先给玩家预览
            userWorldEventLocation.putBoxReward(worldEvent.takeBoxReward());
        }
        userWorldEventLocation.update();
    }

    // 根据怪兽等级确定怪兽的战力  大世界中怪物实力的随机混淆比例，参数1下限，参数2上限，最终实力=基础实力*random(90,150)/100，向下取整
    public static long getMonsterForces(int worldEventId, int monsterLevel) {
        WorldEvent worldEvent = getWorldEvent(worldEventId);
        int random = Tool.getIntRandom(10000);// 返回0-9999 中的一个
        if (random < worldEvent.getFightRate()) {// 如配置7000，表示战斗的概率是70%,配置10000表示必定战斗
            WorldMonsterLevel worldMonsterLevel = getWorldMonsterLevel(monsterLevel);
            if (worldMonsterLevel == null) {
                worldMonsterLevel = getWorldMonsterLevel(getMaxMonsterLevel());
            }
            int random2 = Tool.getRandom(AllParam.WORLD_MONSTER_POWER_RANDOM[0], AllParam.WORLD_MONSTER_POWER_RANDOM[1]);
            //System.out.println("random2:" + random2 + " level:" + worldMonsterLevel.getLevel() + " power:" + worldMonsterLevel.getMonsterPower());
            long result = worldMonsterLevel.getMonsterPower() * random2 / 10000;

            return (long) Math.floor(result);
        } else {
            return 0L;// 无需战斗
        }
    }

    // 根据距离确定单程时间 4,15000,30,9000,11000 计算调节行军距离与时间关系的算法的5个常数，从左到右分别是 公式常数的参数A、K、C、D、U，行军往返时间=（距离格数N^A*K+C)*RANDOM(D,U)
    public static int getOneWayTime(int distance) {
        int A = AllParam.WORLD_EVENT_CONSTANT[0];
        int K = AllParam.WORLD_EVENT_CONSTANT[1];
        int C = AllParam.WORLD_EVENT_CONSTANT[2];
        int D = AllParam.WORLD_EVENT_CONSTANT[3];
        int U = AllParam.WORLD_EVENT_CONSTANT[4];

        //System.out.println("A:" + A + " K:" + K + " C:" + C + " D:" + D + " U:" + U);
        double time = Math.pow(distance, A);// 距离格数N^A*K+C)*RANDOM(D,U)
        time = (time * K)/10000 + C;
        time = time * Tool.getRandom(D, U) / 10000;
        return (int) Math.floor(time);
    }

    //  清空事件队列，0点8点16点整点刷新，雷达升级刷新，使用道具刷新，都需要清空
    public static void clearWorldQueue(long userId) {
        EntityManager.delEntity(UserWorldEventQueue.class, userId);
    }

    // 清除事件 0点8点16点整点刷新,不清除事件，雷达升级刷新和使用道具刷新，需要清除事件
    public static void clearWorldEventLocation(long userId) {
        for (UserWorldEventLocation userWorldEventLocation : getUserWorldEventLocationList(userId)) {
            if (userWorldEventLocation.isNotTeamMarch()) {
                userWorldEventLocation.cleanData();
                userWorldEventLocation.update();
            }
        }
    }

    // 随机生成事件，放在事件队列里。先随机确定品质，再根据groupid,和品质，确定事件池子，进行随机。 再随机确定事件的距离
    public static void creatEvent(long userId, WorldRadar worldRadar, long eventTime) {
        for (int i=0;i<worldRadar.getEventRefreshNum();i++) {
            creatEventEueqe(userId, worldRadar, eventTime);
        }
    }

    // 随机生成事件，放在事件队列里。先随机确定品质，再根据groupid,和品质，确定事件池子，进行随机。 再随机确定事件的距离
    public static UserWorldEventQueue creatEventEueqe(long userId, WorldRadar worldRadar, long eventTime) {
        int quality = getRandomQuality(worldRadar);// 随机到的品质
        List<WorldEvent> worldEventList = getWoldEventByGroupIdAndQuality(worldRadar.getEventGroup(), quality);
        if (worldEventList.size() == 0) {
            logger.info("###creatEvent error radar level:" + worldRadar.getLevel() + " groupId:" + worldRadar.getEventGroup() + " quality:" + quality);
            return null;
        }
        int random = Tool.getIntRandom(worldEventList.size());
        WorldEvent worldEvent = worldEventList.get(random);// 随机到的事件
        int distance = getRandomDistance(worldEvent);// 事件距离
        UserWorldEventQueue userWorldEventQueue = new UserWorldEventQueue(userId, worldEvent.getId(), eventTime, distance);
        userWorldEventQueue.doCreate();
        return userWorldEventQueue;// 返回随机出来的事件队列对象
    }

    // 随机获得事件的品质，品质是用来做分类用而已
    public static int getRandomQuality(WorldRadar worldRadar) {
        int[] rates = CommonUtils.strToIntArray(worldRadar.getEventRate());
        return CommonUtils.getRandom(rates);// 随机到的品质1-6
    }

    // 随机获得事件的距离
    public static int getRandomDistance(WorldEvent worldEvent) {
        int[] rates = CommonUtils.strToIntArray(worldEvent.getDistanceRate());
        return CommonUtils.getRandom(rates);// 随机到的距离
    }

    // 判断是否需要重新刷新事件，当前时间距离上次的时间是否跨过了0.8.16点
    public static boolean checkIsNeedRefreshEvent(long lastTime) {
        long curTime = TimeUtils.getCurTime();
        if (curTime - lastTime >= WorldConstant.WORLD_EVENT_REFRESH_DIFFTIME || curTime < lastTime) {
            return true;// 超过8个小时，要刷新
        }

        long sTime = TimeUtils.getStartTimeOfDay(curTime);// 今天的0点0分0秒
        long diff = WorldConstant.WORLD_EVENT_REFRESH_DIFFTIME;// 间隔8小时
        for (int i=0; i<3; i++) {
            long eventTime = sTime + diff * i;// 今天的0点，8点，16点
            if (lastTime < eventTime && curTime >= eventTime) {
                return true;
            }
        }

        return false;
    }

    // 获得100042时，解锁行军队列
    public static void unlockTeam(PBUserData.Builder pbUserData, UserInfo userInfo, int num, Object... logParams){
        if (num < 1) {
            return;
        }
        long userId = userInfo.getId();
        int curNum = countTeamNum(userId);// 当前永久行军队列的数量
        if (curNum >= AllParam.WORLD_MARCH_NUM) {
            return;// 已经达到上限
        }

        int addNum = Math.min(num, AllParam.WORLD_MARCH_NUM - curNum);
        if (addNum > 0) {
            for (int i=0; i<addNum; i++) {
                int teamId = curNum + i + 1;// 队列id，1-5，获得某个道具解锁一个
                UserWorldTeam userWorldTeam = new UserWorldTeam();
                userWorldTeam.putUserId(userId);
                userWorldTeam.putTeamId(teamId);
                userWorldTeam.doCreate();
                ProtoDataUtils.updatePBUserData(pbUserData, userWorldTeam, true);
            }
        }
    }

    // 获得100045 时， 增加临时行军队列的有效时间
    public static void addTmpTeamValidTime(PBUserData.Builder pbUserData, UserInfo userInfo, int num) {
        UserWorldTeam userWorldTeam = getUserWorldTeam(userInfo.getId(), WorldConstant.WORLD_TEAM_TMP);
        if (userWorldTeam == null) {
            if (countTeamNum(userInfo.getId()) >= AllParam.WORLD_MARCH_NUM) {
                return;// 已经达到上限，不能创建临时行军队列
            }

            userWorldTeam = new UserWorldTeam();
            userWorldTeam.putUserId(userInfo.getId());
            userWorldTeam.putTeamId(WorldConstant.WORLD_TEAM_TMP);
            userWorldTeam.putValidTime(TimeUtils.getCurTime() + AllParam.WORLD_TEMP_MARCH_TIME * num);
            userWorldTeam.doCreate();
            ProtoDataUtils.updatePBUserData(pbUserData, userWorldTeam, true);
        } else {
            long newValidTime = userWorldTeam.getValidTime() + AllParam.WORLD_TEMP_MARCH_TIME * num;
            if (userWorldTeam.getValidTime() < TimeUtils.getCurTime()) {//  之前已经过期了，则从当前时间开始计算
                newValidTime = TimeUtils.getCurTime() + AllParam.WORLD_TEMP_MARCH_TIME * num;
            }
            userWorldTeam.putValidTime(newValidTime);
            userWorldTeam.update();
            ProtoDataUtils.updatePBUserData(pbUserData, userWorldTeam);
        }
    }

    // 当前永久的行军队列数量
    public static int countTeamNum(long userId) {
        List<UserWorldTeam> list = getUserWorldTeamList(userId);
        int count = 0;
        for (UserWorldTeam userWorldTeam : list) {
            if (!userWorldTeam.isTmp()) {
                count++;
            }
        }
        return count;
    }

    // 兵力加成 =（ 派遣兵力 - 兵力下限）/兵力加成段 不能超过上限50%（5000）
    public static int getWorldPowerAdd(WorldEvent worldEvent, int soldier) {
        double addSoldier = soldier - worldEvent.getTroopsLimit();//
        double addValue = addSoldier / worldEvent.getTroopsRatio() * 10000;
        double result = Math.min(addValue, AllParam.WORLD_TROOPS_ADD_MAX);
        return (int) result;
    }

    // 把第一条事件更新到事件坑位里，确定怪兽等级，随机怪兽战力, 删除事件队列里的数据
    public static void updateEventLocationFromEventQueue(UserInfo userInfo, UserWorldInfo userWorldInfo, UserWorldEventLocation userWorldEventLocation) {
        userWorldEventLocation.cleanData();

        long userId = userInfo.getId();
        String orderListKey = EntityManager.getOrderListKey("UserWorldEventQueue." + userId);
        String firstId = Future.await(RedisTools.lpop(orderListKey));// 第一条数据, 没数据时返回空字符串
        long dbid = 0L;
        try {
            dbid = Long.parseLong(firstId);// dbid
        } catch (Exception e) {}
        UserWorldEventQueue userWorldEventQueue = getUserWorldEventQueue(userId, dbid);
        if (userWorldEventQueue != null) {
            putEventToEventLocation(userWorldInfo, userWorldEventQueue, userWorldEventLocation);// 把事件更新到事件坑位里，确定怪兽等级，随机怪兽战力, 删除事件队列里的数据
            userWorldEventQueue.delete();
        }
    }

    // 生成战报
    public static void createWorldRecord(UserInfo userInfo, UserWorldTeam userWorldTeam) {
        if (userWorldTeam.getResult() == WorldConstant.WORLD_EVENT_RESULT_999) {
            return;// 撤退返回，不生成战报
        }
        UserWorldRecord userWorldRecord = new UserWorldRecord();
        userWorldRecord.putUserId(userInfo.getId());
        userWorldRecord.putEventId(userWorldTeam.getEventId());
        userWorldRecord.putPower(userWorldTeam.getPower());
        userWorldRecord.putForces(userWorldTeam.getForces());
        userWorldRecord.putPartner(userWorldTeam.getPartner());
        userWorldRecord.putSoldier(userWorldTeam.getSoldier());
        userWorldRecord.putSoldierWound(userWorldTeam.countSoldierWound());
        userWorldRecord.putResult(userWorldTeam.getResult());
        userWorldRecord.putStartTime(userWorldTeam.getStartTime());
        userWorldRecord.putReward(userWorldTeam.takeFightReward());
        userWorldRecord.putParam(userWorldTeam.getParam());
        userWorldRecord.putLocationId(userWorldTeam.getLocationId());
        userWorldRecord.putRandomReward(userWorldTeam.getRandomReward());
        userWorldRecord.doCreate();

        sendRedPrompt(userInfo.getId(), userWorldRecord);// 行军报告有奖励时，推送红点提示,
    }

    /**
     * 更新队伍派遣的干员信息
     * @param teamId =0 表示干员返回了
     */
    public static void updateWorldPartner(PBUserData.Builder pbUserData, UserInfo userInfo, JsonArray partnerIds, int teamId) {
        for (int i=0; i<partnerIds.size(); i++) {
            int partnerId = partnerIds.getInteger(i);
            UserWorldPartner userWorldPartner = WorldManager.getUserWorldPartner(userInfo.getId(), partnerId);
            userWorldPartner.putTeamId(teamId);
            userWorldPartner.update();
            ProtoDataUtils.updatePBUserData(pbUserData, userWorldPartner);// 更新干员信息
        }
    }



    /**
     * 行军到达事件地点时处理的事务
     * @param isUseItem 是否使用道具，使用道具到达时间是当前时间，自然到达时间是updatetime+timeleft
     */
    public static void handleTeamArrive(PBUserData.Builder pbUserData, UserInfo userInfo, UserWorldInfo userWorldInfo, UserWorldTeam userWorldTeam, UserWorldEventLocation userWorldEventLocation, boolean isUseItem) {
        long userId = userInfo.getId();
        int eventResult = 0;// 事件触发结果
        long arriveTime = isUseItem ? TimeUtils.getCurTime() : userWorldTeam.countArriveTime();// 使用道具到达时间是当前时间，自然到达时间是updatetime+timeleft
        UserWorldEventLocationTeam userWorldEventLocationTeam = getUserWorldEventLocationTeam(userId, userWorldEventLocation.getLocationId(), userWorldTeam.getTeamId());

        if (checkLocationTeamExpire(userWorldEventLocationTeam, userWorldEventLocation, arriveTime)) {// 事件未过期，有效
            eventResult = handleEventFight(pbUserData, userInfo, userWorldInfo, userWorldTeam, userWorldEventLocation);// 处理战斗结果
        }

        int newOneWayTime = WorldManager.takeReturnTime(userWorldTeam.getOnewayTime());//返程的时间 过去途中耗时的50%，300表示返程时间上限300秒
        userWorldTeam.putResult(eventResult);
        userWorldTeam.putStatus(WorldConstant.WORLD_TEAM_STATUS_2);// 改成返回状态
        userWorldTeam.putTimeLeft(newOneWayTime);
        userWorldTeam.putOnewayTime(newOneWayTime);
        userWorldTeam.putUpdateTime(arriveTime);
        userWorldTeam.putHelpDbid(0L);// 返程也可以发起求助
        userWorldTeam.putUniqeId(Tool.getUUID());

        if (userWorldEventLocationTeam != null) {
            userWorldEventLocationTeam.delete();
            ProtoDataUtils.deletePBUserData(pbUserData, userWorldEventLocationTeam);
        }
    }

    // 到达事件点后，进行战斗，更新事件数据
    public static int handleEventFight(PBUserData.Builder pbUserData, UserInfo userInfo, UserWorldInfo userWorldInfo, UserWorldTeam userWorldTeam, UserWorldEventLocation userWorldEventLocation) {
        long userId = userInfo.getId();
        WorldEvent worldEvent = getWorldEvent(userWorldTeam.getEventId());// 事件
        int eventResult = userWorldTeam.countFightResult();// 战斗结果

        if (eventResult == WorldConstant.WORLD_EVENT_RESULT_1) {//本次战斗失败
            // 会影响user_world_info 的怪物等级
            if (userWorldTeam.isChangeMonsterLevel() && userWorldEventLocation.getFailNum() == 0 && userWorldInfo.getMonsterLevel() > 1) {// 未降级过
                userWorldInfo.putMonsterLevel(userInfo, userWorldInfo.getMonsterLevel() - 1);
                userWorldInfo.update();
                ProtoDataUtils.updatePBUserData(pbUserData, userWorldInfo);
            }

            long forces = userWorldEventLocation.getForces() - userWorldTeam.getPower();
            userWorldEventLocation.putForces(forces);// 怪兽的剩余兵力
            userWorldEventLocation.addFailNum(1);// 失败次数+1
        } else {// 战斗胜利需要更新事件坑位
            if (userWorldTeam.isChangeMonsterLevel() && eventResult == WorldConstant.WORLD_EVENT_RESULT_3) {// 本次战斗完美击败，怪兽等级+1
                userWorldInfo.putMonsterLevel(userInfo, userWorldInfo.getMonsterLevel() + 1);
                userWorldInfo.update();
                ProtoDataUtils.updatePBUserData(pbUserData, userWorldInfo);
            }
            handleFightSucc(pbUserData, userInfo, userWorldInfo, userWorldTeam, userWorldEventLocation);// 到达事件点后，战斗胜利，根据事件类型是否刷新事件坑位的数据
        }

        userWorldEventLocation.update();
        ProtoDataUtils.updatePBUserData(pbUserData, userWorldEventLocation);

        if (worldEvent.getType() == WorldConstant.WORLD_EVENT_TYPE_4) {// 搜救事件获得秘书碎片，要判断一下是否已经获得对应秘书，客户端表现用
            List<RewardItem> rewardItemList = CommonUtils.takeReawrdItemFromStr(userWorldTeam.getRandomReward());
            if (rewardItemList.size() > 0) {
                int chipId = rewardItemList.get(0).getItemId();// 秘书碎片id
                boolean isGetSecr = SecretaryManager.checkSecrByChipId(userId, chipId);
                userWorldTeam.putParam(isGetSecr ? "1" : "0");// 1表示已经获得秘书了
            }
        }

        return eventResult;
    }

    // 到达事件点后，战斗胜利，根据事件类型是否刷新事件坑位的数据
    public static void handleFightSucc(PBUserData.Builder pbUserData, UserInfo userInfo, UserWorldInfo userWorldInfo, UserWorldTeam userWorldTeam, UserWorldEventLocation userWorldEventLocation) {

        MainTaskManager.addTaskProgress(pbUserData, userInfo, MainTaskConstant.TASK_TYPE_252, 1);// 累计完成{0}次事件

        int eventId = userWorldTeam.getEventId();
        WorldEvent worldEvent = getWorldEvent(eventId);
        if (worldEvent != null) {
            MainTaskManager.addTaskProgress(pbUserData, userInfo, MainTaskConstant.TASK_TYPE_69, 1, worldEvent.getType());// 完成{1}次{0}事件
            MainTaskManager.addTaskProgress(pbUserData, userInfo, MainTaskConstant.TASK_TYPE_253, 1, worldEvent.getType());// 累计完成{1}次{0}事件
        }

        if (userWorldEventLocation.isCommonType()) {// 事件来源类型1-系统整点自动刷新，locationid 从1--包括讨伐事件，搜救事件，采集事件，
            WorldManager.updateEventLocationFromEventQueue(userInfo, userWorldInfo, userWorldEventLocation);
        } else if (userWorldEventLocation.isOfferType()) {// 事件来源悬赏事件，locationid 固定2001
            updateEventOffer(userWorldEventLocation, eventId + 1); // 下个悬赏事件
        } else if (userWorldEventLocation.isUrgentType() || userWorldEventLocation.isExtraType()) {// 特殊道具刷出来的额外事件或是紧急事件，完成后直接清除数据
            userWorldEventLocation.cleanData();
        }
    }

    /**
     * 行军报告有奖励时，推送红点提示,
      */
    private static void sendRedPrompt(long userId, UserWorldRecord userWorldRecord) {
        if (userWorldRecord == null) {
            return;
        }
        if (userWorldRecord.hasReward()) {
            CSGameSystemRedPointResponse.Builder builder = CSGameSystemRedPointResponse.newBuilder();
            CsGameSystem.RedPoint_World.Builder redPointWorld = CsGameSystem.RedPoint_World.newBuilder();
            redPointWorld.setNewRecord(true);

            builder.setWorld(redPointWorld);
            builder.setPromptType(SystemConstant.RED_POINT_WORLD_RECORD);
            JsonObject jsonObject = ResponseUtils.getRedpointResponseData( builder.build().toByteArray());
            GameUser.pushToUser(userId, jsonObject);
        }
    }

    // 判断是否有未领取，且有奖励的战报
    public static boolean checkRedPoint(UserInfo userInfo, CSGameSystemRedPointResponse.Builder builder) {
        List<UserWorldRecord> list = getUserWorldRecordList(userInfo.getId());
        CsGameSystem.RedPoint_World.Builder redPointWorld = CsGameSystem.RedPoint_World.newBuilder();
        for (UserWorldRecord userWorldRecord : list) {
            if (!userWorldRecord.isGet() && userWorldRecord.hasReward()) {
                redPointWorld.setNewRecord(true);
                builder.setWorld(redPointWorld);
                return true;// 未领取，且有奖励
            }
        }
        return false;
    }

    // 更新悬赏事件的数据
    public static void updateEventOffer(UserWorldEventLocation userWorldEventLocation, int eventId) {
        WorldEvent worldEvent = getWorldEvent(eventId);
        if (worldEvent == null) {
            return;
        }
        userWorldEventLocation.putEventId(eventId);
        userWorldEventLocation.putTotalForces(worldEvent.getMonsterPower());// 直接取配置表，不走user_world_info 的怪物等级
        userWorldEventLocation.putForces(worldEvent.getMonsterPower());
        int distance = Integer.parseInt(worldEvent.getDistanceRate());
        userWorldEventLocation.putDistance(distance);
        userWorldEventLocation.putOnewayTime(getOneWayTime(distance));
    }

    /**
     * 行军队伍返回到基地, 生成战报, 更新干员派遣信息，更新兵力信息
     */
    public static void handleTeamComplete(PBUserData.Builder pbUserData, UserInfo userInfo, UserWorldTeam userWorldTeam) {
        WorldManager.createWorldRecord(userInfo, userWorldTeam);// 生成战报
        WorldManager.updateWorldPartner(pbUserData, userInfo, userWorldTeam.getPartnerArray(), 0);// // 更新干员信息

        UserCityInfo userCityInfo = BuildManager.getUserCityInfo(userInfo.getId());
        int soldierWound = userWorldTeam.countSoldierWound();//  产生的伤兵数量
        userCityInfo.addSoldierWound(soldierWound);//伤兵数量增加
        userCityInfo.addSoldierBattle(-userWorldTeam.getSoldier());// 战斗中的数量减下来
        userCityInfo.update();
        ProtoDataUtils.updatePBUserData(pbUserData, userCityInfo);

        userWorldTeam.cleanData();// 清空队伍数据
    }

    // 是否可以帮助
    public static boolean isCanHelpWorldTeam(UserWorldTeam userWorldTeam) {
        if (userWorldTeam == null) {
            return false;
        }

        if (userWorldTeam.getStatus() == WorldConstant.WORLD_TEAM_STATUS_0) {
            return false;
        }
        if (userWorldTeam.getHelpDbid() > 0) {
            return false;// 已经发起过了
        }

        return true;
    }

    /**
     * 对行军进行助力，增加扣除的时间，并推送给发起求助的玩家
     * @param userId 发起求助的人
     * @param teamId
     * @param uniqeId
     * @param reduceTime
     * @param userInfoFrom 进行帮助的人
     */
    public static void handleDoHelp(long userId, int teamId, String uniqeId, int reduceTime, UserInfo userInfoFrom, UnionReciprocityList unionReciprocityList) {
        UserWorldTeam userWorldTeam = getUserWorldTeam(userId, teamId);
        if (userWorldTeam == null || !userWorldTeam.getUniqeId().equals(uniqeId)) {
            return;
        }
        userWorldTeam.addReduceTime(reduceTime);
        userWorldTeam.update();

        CsGameSystem.CSGameSystemPushDataResponse.Builder response = CsGameSystem.CSGameSystemPushDataResponse.newBuilder();
        Common.PBUserData.Builder pbUserData = response.getUserBuilder();
        ProtoDataUtils.updatePBUserData(pbUserData, userWorldTeam);

        response.setType(ResponseUtils.PUSH_TIPS_TYPE_1);
        response.setUserId(userInfoFrom.getId());
        response.setUserName(userInfoFrom.getUserName());
        response.addParam(unionReciprocityList.getHelpNum() + "");
        response.addParam(unionReciprocityList.getHelpNumMax() + "");

        JsonObject jsonObject = ResponseUtils.getPushResponseData(response.build().toByteArray());// 广播给其他人的数据
        GameUser.pushToUser(userId, jsonObject);
    }

    // userid 和locationid user_locationid
    public static String getLocationUnique(long userId, int locationid) {
        return userId + "_" + locationid;
    }

    // 创建事件坑位的队伍派遣信息
    public static void createLoactionTeamInfo(PBUserData.Builder pbUserData, long userId, UserWorldEventLocation userWorldEventLocation, UserWorldTeam userWorldTeam) {
        if (userWorldEventLocation == null || userWorldTeam == null || userWorldEventLocation.getStartTime() == 0) {
            return;
        }
        UserWorldEventLocationTeam userWorldEventLocationTeam = new UserWorldEventLocationTeam(userId, userWorldEventLocation.getLocationId(), userWorldTeam.getTeamId(), userWorldEventLocation.getStartTime(), userWorldEventLocation.getEventQueueDbid());
        userWorldEventLocationTeam.doCreate();
        ProtoDataUtils.updatePBUserData(pbUserData, userWorldEventLocationTeam, true);
    }

    // 统计该事件点，目前有多少个队伍在行军
    public static long countLocationTeam(long userId, int locationId) {
        String uniqueId = getLocationUnique(userId, locationId);
        return EntityManager.getEntityCount(UserWorldEventLocationTeam.class, uniqueId);
    }

    // 判断该队伍行军的坑位的事件是否有效，有效返回true, 无效返回false
    public static boolean checkLocationTeamExpire(UserWorldEventLocationTeam userWorldEventLocationTeam, UserWorldEventLocation userWorldEventLocation, long arriveTime) {
        if (userWorldEventLocation.isUrgentType()) {
            return true;// 紧急触发的事件，只要有派兵就会完成事件
        }
        if (userWorldEventLocation.isExpire(arriveTime) || userWorldEventLocationTeam == null) {
            return false;// 已经过期了或是被完成
        }
        if (userWorldEventLocationTeam.getLocationId() == userWorldEventLocation.getLocationId() && userWorldEventLocationTeam.getEventQueueDbid() == userWorldEventLocation.getEventQueueDbid()) {
            return true;
        } else {
            return false;// 不是同个事件了
        }
    }

    // 计算返程的时间 过去途中耗时的50%，300表示返程时间上限300秒
    public static int takeReturnTime(int oneWayTime) {
        int time = oneWayTime * AllParam.WORLD_MARCH_RETURN_TIME[0] / 10000;
        return Math.min(time, AllParam.WORLD_MARCH_RETURN_TIME[1]);
    }

    // 解锁大世界行军的悬赏事件
    public static UserWorldEventLocation unlockOfferEvent(UserInfo userInfo) {
        if (!SystemManager.checkModuleOpen(userInfo, SystemConstant.SYSTEM_MODULE_ID_WORLD_EVENT_OFFER)) {
            return null;// 雷达等级不足,未解锁悬赏事件
        }
        long userId = userInfo.getId();
        int locationId = WorldConstant.WORLD_EVENT_LOCATION_ID_OFFER;
        UserWorldEventLocation userWorldEventLocation = getUserWorldEventLocation(userId, locationId);
        if (userWorldEventLocation != null) {
            return null;// 已经解锁过了
        }
        userWorldEventLocation = new UserWorldEventLocation(userId, locationId, WorldConstant.WORLD_EVENT_LOCATION_TYPE_5);
        int eventId = getWorldEventOfferFirstId();// 悬赏事件的初始id
        updateEventOffer(userWorldEventLocation, eventId);
        userWorldEventLocation.doCreate();
        return userWorldEventLocation;
    }

    // 出征的时候有概率触发紧急事件
    public static void creatUrgentEvent(PBUserData.Builder pbUserData, UserInfo userInfo, UserWorldInfo userWorldInfo) {
        long userId = userInfo.getId();
        UserBuild userBuild = BuildManager.getUserBuild(userId, BuildConstant.BUILD_ID_RADAR);
        WorldRadar worldRadar = WorldManager.getWorldRadar(userBuild.getLevel());
        if (worldRadar == null) {
            return;
        }
        int curRefreshNum = userWorldInfo.getUrgentRefreshNum();// 今日已刷新的次数
        if (curRefreshNum >= worldRadar.getUrgentEventLimit()) {
            return;// 次数达到上限
        }
        List<UserWorldEventLocation> locationList = getUserWorldEventLocationListUrgent(userId, userWorldInfo);// 获取紧急事件的坑位列表
        for (UserWorldEventLocation userWorldEventLocation : locationList) {
            if (!userWorldEventLocation.isNotTeamMarch()) {
                return;// 只要有1个坑位，还有队伍在行军，就不进行刷新
            }
        }

        int random = Tool.getIntRandom(10000);// 则返回0-9999中的一个 配置500 表示概率5%
        if (worldRadar.getUrgentEventRate() == 0 || random >= worldRadar.getUrgentEventRate()) {
            return;// 概率没中
        }

        // 从对应的groupid事件里随机一个事件
        List<WorldEvent> worldEventList = getWoldEventByGroupId(worldRadar.getUrgentEventGroup());
        if (worldEventList.size() == 0) {
            return;
        }
        // 事件数量为玩家当前拥有的队列数+1，且不超过队列最大上限 改成每次只触发1个
        long curTime = TimeUtils.getCurTime();
        int eventNum = 1;//
//        int eventNum = countTeamNum(userId) + 1;
//        eventNum = Math.min(eventNum, AllParam.WORLD_MARCH_NUM);
        for (int i=0; i<eventNum; i++) {
            int locationId = WorldConstant.WORLD_EVENT_LOCATION_ID_URGENT + i;
            UserWorldEventLocation userWorldEventLocation = getUserWorldEventLocation(userId, locationId);
            if (userWorldEventLocation != null) {
                random = Tool.getIntRandom(worldEventList.size());
                WorldEvent worldEvent = worldEventList.get(random);// 随机到的事件
                int distance = getRandomDistance(worldEvent);// 事件距离
                UserWorldEventQueue userWorldEventQueue = new UserWorldEventQueue(userId, worldEvent.getId(), curTime, distance);
                userWorldEventQueue.doCreate();
                WorldManager.putEventToEventLocation(userWorldInfo, userWorldEventQueue, userWorldEventLocation);// 把事件更新到事件坑位里，确定怪兽等级，随机怪兽战力, 删除事件队列里的数据
                ProtoDataUtils.updatePBUserData(pbUserData, userWorldEventLocation, true);// 下发全部信息，客户端需要用
                userWorldEventQueue.delete();
            }
        }

        userWorldInfo.addUrgentRefreshNum(1);// 刷新次数+1
    }

    /**
     * 大世界行军技能的总加成
     * @param userPartnerList 上阵的干员列表
     * @param effectType 加成类型
     * @param charType 性格0，表示全部 作用对象的性格
     * @return
     */
    public static int countWorldPowerAdd(List<UserPartner> userPartnerList, int effectType, int charType) {
        int total = 0;
        for (UserPartner userPartner : userPartnerList) {
            total += userPartner.countWorldSkillAdd(effectType, charType);
        }
        return total;
    }

    // 计算行军消耗的时间
    public static int countWayTime(List<UserPartner> userPartnerList, int oneWayTime) {
        int skillAdd = WorldManager.countWorldPowerAdd(userPartnerList, SkillConstant.EFFECT_TYPE_105, 0);// 总的减少的百分比
        double total = oneWayTime;
        double sub = total * skillAdd / 10000;// 减少的时间
        double result = total - sub;
        return (int)Math.floor(result);
    }

    // 使用 121001; // 使用道具新增一个额外事件，需要显示在哪个坑位上,将当前已随机出事件且未被指派的事件中品质最低的一档里随机1个替换掉
    public static UserWorldEventLocation takeRefreshExtraEvent(long userId) {
        List<UserWorldEventLocation> list = WorldManager.getUserWorldEventLocationList(userId);
        int minQuality = 0;// 最低品质
        for (UserWorldEventLocation userWorldEventLocation : list) {
            if (userWorldEventLocation.isExtraType() && userWorldEventLocation.isNotTeamMarch()) {// 额外事件
                if (userWorldEventLocation.isExpire(TimeUtils.getCurTime()) || userWorldEventLocation.getEventId() == 0) {
                    return userWorldEventLocation;// 优先刷新已到期的坑位
                } else {
                    WorldEvent worldEvent = getWorldEvent(userWorldEventLocation.getEventId());
                    if (worldEvent != null) {
                        if (minQuality == 0 || worldEvent.getQuality() < minQuality) {
                            minQuality = worldEvent.getQuality();
                        }
                    }
                }
            }
        }
        List<UserWorldEventLocation> list2 = new ArrayList<>();
        for (UserWorldEventLocation userWorldEventLocation : list) {
            if (userWorldEventLocation.isExtraType() && userWorldEventLocation.isNotTeamMarch()) {// 额外事件
                WorldEvent worldEvent = getWorldEvent(userWorldEventLocation.getEventId());
                if (minQuality == worldEvent.getQuality()) {
                    list2.add(userWorldEventLocation);
                }
            }
        }
        if (list2.size() == 0) {
            return null;// 全部满了
        } else {
            int random = Tool.getIntRandom(list2.size());
            return list2.get(random);
        }
    }
}
